//go:build (386 && !appengine) || (amd64 && !appengine) || (arm && !appengine) || (arm64 && !appengine) || (ppc64le && !appengine) || (mipsle && !appengine) || (mips64le && !appengine) || (mips64p32le && !appengine) || (wasm && !appengine)// +build 386,!appengine amd64,!appengine arm,!appengine arm64,!appengine ppc64le,!appengine mipsle,!appengine mips64le,!appengine mips64p32le,!appengine wasm,!appenginepackage roaringimport ()func ( *arrayContainer) ( io.Writer) (int, error) { := uint16SliceAsByteSlice(.content)return .Write()}func ( *bitmapContainer) ( io.Writer) (int, error) {if .cardinality <= arrayDefaultMaxSize {return0, errors.New("refusing to write bitmap container with cardinality of array container") } := uint64SliceAsByteSlice(.bitmap)return .Write()}func uint64SliceAsByteSlice( []uint64) []byte {// make a new slice header := *(*reflect.SliceHeader)(unsafe.Pointer(&))// update its capacity and length .Len *= 8 .Cap *= 8// instantiate result and use KeepAlive so data isn't unmapped. := *(*[]byte)(unsafe.Pointer(&))runtime.KeepAlive(&)// return itreturn}func uint16SliceAsByteSlice( []uint16) []byte {// make a new slice header := *(*reflect.SliceHeader)(unsafe.Pointer(&))// update its capacity and length .Len *= 2 .Cap *= 2// instantiate result and use KeepAlive so data isn't unmapped. := *(*[]byte)(unsafe.Pointer(&))runtime.KeepAlive(&)// return itreturn}func interval16SliceAsByteSlice( []interval16) []byte {// make a new slice header := *(*reflect.SliceHeader)(unsafe.Pointer(&))// update its capacity and length .Len *= 4 .Cap *= 4// instantiate result and use KeepAlive so data isn't unmapped. := *(*[]byte)(unsafe.Pointer(&))runtime.KeepAlive(&)// return itreturn}func ( *bitmapContainer) () []byte {returnuint64SliceAsByteSlice(.bitmap)}// Deserialization code follows// //// These methods (byteSliceAsUint16Slice,...) do not make copies,// they are pointer-based (unsafe). The caller is responsible to// ensure that the input slice does not get garbage collected, deleted// or modified while you hold the returned slince.// //func byteSliceAsUint16Slice( []byte) ( []uint16) { // here we create a new slice holderiflen()%2 != 0 {panic("Slice size should be divisible by 2") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / 2 .Cap = .Cap / 2// instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsUint64Slice( []byte) ( []uint64) {iflen()%8 != 0 {panic("Slice size should be divisible by 8") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / 8 .Cap = .Cap / 8// instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsInterval16Slice( []byte) ( []interval16) {iflen()%4 != 0 {panic("Slice size should be divisible by 4") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / 4 .Cap = .Cap / 4// instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsContainerSlice( []byte) ( []container) {varcontainer := int(unsafe.Sizeof())iflen()% != 0 {panic("Slice size should be divisible by unsafe.Sizeof(container)") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / .Cap = .Cap / // instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsBitsetSlice( []byte) ( []bitmapContainer) { := int(unsafe.Sizeof(bitmapContainer{}))iflen()% != 0 {panic("Slice size should be divisible by unsafe.Sizeof(bitmapContainer)") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / .Cap = .Cap / // instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsArraySlice( []byte) ( []arrayContainer) { := int(unsafe.Sizeof(arrayContainer{}))iflen()% != 0 {panic("Slice size should be divisible by unsafe.Sizeof(arrayContainer)") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / .Cap = .Cap / // instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsRun16Slice( []byte) ( []runContainer16) { := int(unsafe.Sizeof(runContainer16{}))iflen()% != 0 {panic("Slice size should be divisible by unsafe.Sizeof(runContainer16)") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / .Cap = .Cap / // instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}func byteSliceAsBoolSlice( []byte) ( []bool) { := int(unsafe.Sizeof(true))iflen()% != 0 {panic("Slice size should be divisible by unsafe.Sizeof(bool)") }// reference: https://go101.org/article/unsafe.html// make a new slice header := (*reflect.SliceHeader)(unsafe.Pointer(&)) := (*reflect.SliceHeader)(unsafe.Pointer(&))// transfer the data from the given slice to a new variable (our result) .Data = .Data .Len = .Len / .Cap = .Cap / // instantiate result and use KeepAlive so data isn't unmapped.runtime.KeepAlive(&) // it is still crucial, GC can free it)// return resultreturn}// FrozenView creates a static view of a serialized bitmap stored in buf.// It uses CRoaring's frozen bitmap format.//// The format specification is available here:// https://github.com/RoaringBitmap/CRoaring/blob/2c867e9f9c9e2a3a7032791f94c4c7ae3013f6e0/src/roaring.c#L2756-L2783//// The provided byte array (buf) is expected to be a constant.// The function makes the best effort attempt not to copy data.// Only little endian is supported. The function will err if it detects a big// endian serialized file.// You should take care not to modify buff as it will likely result in// unexpected program behavior.// If said buffer comes from a memory map, it's advisable to give it read// only permissions, either at creation or by calling Mprotect from the// golang.org/x/sys/unix package.//// Resulting bitmaps are effectively immutable in the following sense:// a copy-on-write marker is used so that when you modify the resulting// bitmap, copies of selected data (containers) are made.// You should *not* change the copy-on-write status of the resulting// bitmaps (SetCopyOnWrite).//// If buf becomes unavailable, then a bitmap created with// FromBuffer would be effectively broken. Furthermore, any// bitmap derived from this bitmap (e.g., via Or, And) might// also be broken. Thus, before making buf unavailable, you should// call CloneCopyOnWriteContainers on all such bitmaps.func ( *Bitmap) ( []byte) error {return .highlowcontainer.frozenView()}/* Verbatim specification from CRoaring. * * FROZEN SERIALIZATION FORMAT DESCRIPTION * * -- (beginning must be aligned by 32 bytes) -- * <bitset_data> uint64_t[BITSET_CONTAINER_SIZE_IN_WORDS * num_bitset_containers] * <run_data> rle16_t[total number of rle elements in all run containers] * <array_data> uint16_t[total number of array elements in all array containers] * <keys> uint16_t[num_containers] * <counts> uint16_t[num_containers] * <typecodes> uint8_t[num_containers] * <header> uint32_t * * <header> is a 4-byte value which is a bit union of frozenCookie (15 bits) * and the number of containers (17 bits). * * <counts> stores number of elements for every container. * Its meaning depends on container type. * For array and bitset containers, this value is the container cardinality minus one. * For run container, it is the number of rle_t elements (n_runs). * * <bitset_data>,<array_data>,<run_data> are flat arrays of elements of * all containers of respective type. * * <*_data> and <keys> are kept close together because they are not accessed * during deserilization. This may reduce IO in case of large mmaped bitmaps. * All members have their native alignments during deserilization except <header>, * which is not guaranteed to be aligned by 4 bytes. */const frozenCookie = 13766var (// ErrFrozenBitmapInvalidCookie is returned when the header does not contain the frozenCookie.ErrFrozenBitmapInvalidCookie = errors.New("header does not contain the frozenCookie")// ErrFrozenBitmapBigEndian is returned when the header is big endian.ErrFrozenBitmapBigEndian = errors.New("loading big endian frozen bitmaps is not supported")// ErrFrozenBitmapIncomplete is returned when the buffer is too small to contain a frozen bitmap.ErrFrozenBitmapIncomplete = errors.New("input buffer too small to contain a frozen bitmap")// ErrFrozenBitmapOverpopulated is returned when the number of containers is too large.ErrFrozenBitmapOverpopulated = errors.New("too many containers")// ErrFrozenBitmapUnexpectedData is returned when the buffer contains unexpected data.ErrFrozenBitmapUnexpectedData = errors.New("spurious data in input")// ErrFrozenBitmapInvalidTypecode is returned when the typecode is invalid.ErrFrozenBitmapInvalidTypecode = errors.New("unrecognized typecode")// ErrFrozenBitmapBufferTooSmall is returned when the buffer is too small.ErrFrozenBitmapBufferTooSmall = errors.New("buffer too small"))func ( *roaringArray) ( []byte) error {iflen() < 4 {returnErrFrozenBitmapIncomplete } := binary.BigEndian.Uint32([len()-4:])if &0x7fff == frozenCookie {returnErrFrozenBitmapBigEndian } := binary.LittleEndian.Uint32([len()-4:]) = [:len()-4]if &0x7fff != frozenCookie {returnErrFrozenBitmapInvalidCookie } := int( >> 15)if > (1 << 16) {returnErrFrozenBitmapOverpopulated }// 1 byte per type, 2 bytes per key, 2 bytes per count.iflen() < 5* {returnErrFrozenBitmapIncomplete } := [len()-:] = [:len()-] := byteSliceAsUint16Slice([len()-2*:]) = [:len()-2*] := byteSliceAsUint16Slice([len()-2*:]) = [:len()-2*] , , := 0, 0, 0 , := 0, 0for , := range {switch {case1: ++case2: ++ += int([]) + 1case3: ++ += int([])default:returnErrFrozenBitmapInvalidTypecode } }iflen() < (1<<13)*+4*+2* {returnErrFrozenBitmapIncomplete } := byteSliceAsUint64Slice([:(1<<13)*]) = [(1<<13)*:] := byteSliceAsInterval16Slice([:4*]) = [4*:] := byteSliceAsUint16Slice([:2*]) = [2*:]iflen() != 0 {returnErrFrozenBitmapUnexpectedData }varcontainer := int(unsafe.Sizeof()) * := int(unsafe.Sizeof(bitmapContainer{})) * := int(unsafe.Sizeof(arrayContainer{})) * := int(unsafe.Sizeof(runContainer16{})) * := int(unsafe.Sizeof(true)) * := + + + + := make([]byte, ) := byteSliceAsContainerSlice([:]) = [:] := byteSliceAsBitsetSlice([:]) = [:] := byteSliceAsArraySlice([:]) = [:] := byteSliceAsRun16Slice([:]) = [:] := byteSliceAsBoolSlice() , , := 0, 0, 0for , := range { [] = trueswitch {case1: [] = &[] [].cardinality = int([]) + 1 [].bitmap = [:1024] = [1024:] ++case2: [] = &[] := int([]) + 1 [].content = [:] = [:] ++case3: [] = &[] [].iv = [:[]] = [[]:] ++ } }// Not consuming the full input is a bug.if != || len() != 0 || != || len() != 0 || != || len() != 0 {panic("we missed something") } .keys = .containers = .needCopyOnWrite = .copyOnWrite = truereturnnil}// GetFrozenSizeInBytes returns the size in bytes of the frozen bitmap.func ( *Bitmap) () uint64 { , , := uint64(0), uint64(0), uint64(0)for , := range .highlowcontainer.containers {switch v := .(type) {case *bitmapContainer: ++case *arrayContainer: += uint64(len(.content))case *runContainer16: += uint64(len(.iv)) } }return4 + 5*uint64(len(.highlowcontainer.containers)) + ( << 13) + 2* + 4*}// Freeze serializes the bitmap in the CRoaring's frozen format.func ( *Bitmap) () ([]byte, error) { := .GetFrozenSizeInBytes() := make([]byte, ) , := .FreezeTo()return , }// FreezeTo serializes the bitmap in the CRoaring's frozen format.func ( *Bitmap) ( []byte) (int, error) { := .highlowcontainer.containers := len() , , := 0, 0, 0for , := range {switch v := .(type) {case *bitmapContainer: ++case *arrayContainer: += len(.content)case *runContainer16: += len(.iv) } } := 4 + 5* + (1<<13)* + 4* + 2*iflen() < {return0, ErrFrozenBitmapBufferTooSmall } := byteSliceAsUint64Slice([:(1<<13)*]) = [(1<<13)*:] := byteSliceAsInterval16Slice([:4*]) = [4*:] := byteSliceAsUint16Slice([:2*]) = [2*:] := byteSliceAsUint16Slice([:2*]) = [2*:] := byteSliceAsUint16Slice([:2*]) = [2*:] := [:] = [:] := uint32(frozenCookie | ( << 15))binary.LittleEndian.PutUint32([:4], )copy(, .highlowcontainer.keys[:])for , := range {switch v := .(type) {case *bitmapContainer:copy(, .bitmap) = [1024:] [] = uint16(.cardinality - 1) [] = 1case *arrayContainer:copy(, .content) = [len(.content):] := len(.content) [] = uint16( - 1) [] = 2case *runContainer16:copy(, .iv) := len(.iv) = [:] [] = uint16() [] = 3 } }return , nil}// WriteFrozenTo serializes the bitmap in the CRoaring's frozen format.func ( *Bitmap) ( io.Writer) (int, error) {// FIXME: this is a naive version that iterates 4 times through the // containers and allocates 3*len(containers) bytes; it's quite likely // it can be done more efficiently. := .highlowcontainer.containers := 0for , := range { , := .(*bitmapContainer)if ! {continue } , := .Write(uint64SliceAsByteSlice(.bitmap)) += if != nil {return , } }for , := range { , := .(*runContainer16)if ! {continue } , := .Write(interval16SliceAsByteSlice(.iv)) += if != nil {return , } }for , := range { , := .(*arrayContainer)if ! {continue } , := .Write(uint16SliceAsByteSlice(.content)) += if != nil {return , } } , := .Write(uint16SliceAsByteSlice(.highlowcontainer.keys)) += if != nil {return , } := make([]byte, 3*len()) := byteSliceAsUint16Slice([:2*len()]) := [2*len():]for , := range {switch c := .(type) {case *bitmapContainer: [] = uint16(.cardinality - 1) [] = 1case *arrayContainer: := len(.content) [] = uint16( - 1) [] = 2case *runContainer16: := len(.iv) [] = uint16() [] = 3 } } , = .Write() += if != nil {return , } := uint32(frozenCookie | (len() << 15))if := binary.Write(, binary.LittleEndian, ); != nil {return , } += 4return , nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.